fix(logs): compact stats panel and collapse filters to reduce above-fold space#788
fix(logs): compact stats panel and collapse filters to reduce above-fold space#788NieiR wants to merge 7 commits intoding113:devfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough本次变更重构并紧凑化日志页面 UI:添加可折叠的筛选区并显示活跃筛选计数、将统计面板改为紧凑内联布局、并为活跃会话列表增加空状态的紧凑渲染选项(compactEmpty)。 Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @NieiR, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 此拉取请求旨在优化使用记录页面的布局,通过减少统计汇总、筛选条件和活跃会话三个主要板块在首屏所占用的空间,使用户无需滚动即可查看到请求记录。这些改进提升了页面的视觉紧凑性和用户体验。 Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review Summary
This PR optimizes the usage logs page layout to reduce above-fold space consumption. The changes are well-structured, focused, and follow React best practices.
PR Size: M
- Lines changed: 323 (154 additions, 169 deletions)
- Files changed: 4
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Key Changes Reviewed
-
Stats Panel Compact Layout ()
- Refactored from 4-card grid to single-line inline display
- Removed unused import
- New and helper components are clean and focused
-
Collapsible Filters ()
- Added wrapper with badge
- Proper use of for to prevent unnecessary re-renders
- Correct dependency array handling for calculation
-
Active Sessions Compact Empty State ()
- New prop (default ) maintains backward compatibility
- Early return pattern for compact empty state is clean
-
Integration ()
- Simple prop addition to enable compact mode
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - Proper error states maintained
- Type safety - TypeScript types are correct
- Documentation accuracy - Comments updated appropriately
- Test coverage - N/A (UI changes, existing tests sufficient)
- Code clarity - Well-structured and readable
Conclusion
No issues found. The PR is well-implemented, follows React best practices, and maintains backward compatibility. The changes are focused and achieve the stated goal of reducing above-fold space consumption.
Automated review by Claude AI
There was a problem hiding this comment.
Code Review Summary
This PR optimizes the usage logs page layout to reduce above-fold space consumption. The changes are well-structured, focused, and follow React best practices.
PR Size: M
- Lines changed: 323 (154 additions, 169 deletions)
- Files changed: 4
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Key Changes Reviewed
Stats Panel Compact Layout
- Refactored from 4-card grid to single-line inline display
- Removed unused cn import
- New StatItem and Separator helper components are clean
Collapsible Filters
- Added Collapsible wrapper with activeFilterCount badge
- Proper use of useMemo for statsFilters to prevent re-renders
- Correct dependency array handling for activeFilterCount
Active Sessions Compact Empty State
- New compactEmpty prop (default false) maintains backward compatibility
- Early return pattern for compact empty state is clean
Integration
- Simple prop addition to enable compact mode
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - Proper error states maintained
- Type safety - TypeScript types are correct
- Documentation accuracy - Comments updated appropriately
- Test coverage - N/A (UI changes, existing tests sufficient)
- Code clarity - Well-structured and readable
Conclusion
No issues found. The PR is well-implemented, follows React best practices, and maintains backward compatibility. The changes are focused and achieve the stated goal of reducing above-fold space consumption.
src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
Outdated
Show resolved
Hide resolved
src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
Outdated
Show resolved
Hide resolved
b565894 to
73c3102
Compare
Additional Comments (1)
The PR description states "改用 In practice this doesn't cause redundant API calls because Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! Prompt To Fix With AIThis is a comment left during a code review.
Path: src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
Line: 266:278
Comment:
**`statsFilters` is not memoized despite PR description**
The PR description states "改用 `useMemo` 保证引用稳定" (use `useMemo` to ensure stable reference), but `statsFilters` is still a plain object literal that gets recreated on every render. This means a new object reference is passed to `UsageLogsStatsPanel` each time.
In practice this doesn't cause redundant API calls because `UsageLogsStatsPanel` internally uses `JSON.stringify(filters)` as a change-detection key. However, it does cause unnecessary re-renders of `UsageLogsStatsPanel` itself (React will re-render the child because the prop reference changed). Consider wrapping in `useMemo` to match the stated intent:
```suggestion
const statsFilters = useMemo(() => ({
userId: filters.userId,
keyId: filters.keyId,
providerId: filters.providerId,
sessionId: filters.sessionId,
startTime: filters.startTime,
endTime: filters.endTime,
statusCode: filters.statusCode,
excludeStatusCode200: filters.excludeStatusCode200,
model: filters.model,
endpoint: filters.endpoint,
minRetryCount: filters.minRetryCount,
}), [filters]);
```
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/customs/active-sessions-list.tsx (1)
14-20:⚠️ Potential issue | 🟡 Minor
fetchActiveSessions中的错误信息为硬编码中文字符串Line 17 的
"获取活跃 Session 失败"是硬编码中文。虽然这是预先存在的代码且错误不会直接渲染到 UI(useQuery的error未在组件中展示),但如果未来 error boundary 或日志系统展示此消息,将无法国际化。此为已有问题,非本 PR 引入,可后续处理。
🧹 Nitpick comments (3)
src/app/[locale]/dashboard/logs/_components/usage-logs-stats-panel.tsx (2)
36-60:filtersKey去重机制被loadStats依赖破坏
loadStats的useCallback依赖了filters对象引用(line 55),而useEffect同时依赖filtersKey和loadStats(line 60)。当父组件未正确 memoizefilters时,即使filtersKey(JSON 字符串)不变,filters引用变化也会导致loadStats重建,进而触发 effect 重新执行,造成重复请求。虽然 PR 在父组件中加了
useMemo来稳定引用,但本组件自身的防护是不完整的。更健壮的做法是让loadStats内部基于filtersKey解析 filters,或使用 ref 模式彻底解耦:建议的修复方案
- const filtersKey = JSON.stringify(filters); - - const loadStats = useCallback(async () => { - setIsLoading(true); - setError(null); - - try { - const result = await getUsageLogsStats(filters); + const filtersKey = JSON.stringify(filters); + const filtersRef = useRef(filters); + filtersRef.current = filters; + + const loadStats = useCallback(async () => { + setIsLoading(true); + setError(null); + + try { + const result = await getUsageLogsStats(filtersRef.current); if (result.ok && result.data) { setStats(result.data); } else { setError(!result.ok ? result.error : t("logs.error.loadFailed")); } } catch (err) { console.error("Failed to load usage logs stats:", err); setError(t("logs.error.loadFailed")); } finally { setIsLoading(false); } - }, [filters, t]); + }, [t]); // biome-ignore lint/correctness/useExhaustiveDependencies: filtersKey is used to detect filter changes useEffect(() => { loadStats(); }, [filtersKey, loadStats]);需要在 line 5 的 import 中添加
useRef。
133-135:Separator命名与 UI 组件库同名,建议区分项目中
@/components/ui/separator.tsx已导出Separator组件。此处的本地函数虽未产生运行时冲突,但同名易造成混淆——未来如果需要在本文件引入 UI 库的Separator,会产生命名冲突。建议重命名为StatDivider或类似名称。src/components/customs/active-sessions-list.tsx (1)
45-53: 建议:compactEmpty放置位置与其他 boolean props 不一致
compactEmpty = false在解构中位于maxHeight之后、showTokensCost之前(line 50),但在接口定义中位于showTokensCost之后(line 33-34)。建议保持解构顺序与接口定义顺序一致,提升可读性。建议调整解构顺序
export function ActiveSessionsList({ currencyCode = "USD", maxItems, showHeader = true, maxHeight = "200px", - compactEmpty = false, showTokensCost = true, + compactEmpty = false, className = "", }: ActiveSessionsListProps) {
…old space Rebased on upstream v0.5.8 (includes hasStatsFilters perf guard). Retains our UX optimizations for issue ding113#766: - Stats panel: compact single-line inline layout (~40px vs ~150px) - Filter criteria: collapsed by default with active filter count badge - Active sessions: compactEmpty single-line when no sessions
73c3102 to
5cc234d
Compare
…ailure (ding113#800) Add parentheses around date arithmetic in buildDateCondition to prevent AT TIME ZONE from binding to INTERVAL instead of the full expression.
src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
Show resolved
Hide resolved
Response to CodeRabbit nitpick suggestions1.
|
…auto-expand on URL filters - Wrap statsFilters in useMemo to prevent unnecessary API re-requests when parent re-renders without filter changes - Replace Object.values filter count with explicit per-field counting, grouping statusCode/excludeStatusCode200 as one filter (consistent with usage-logs-filters.tsx) - Auto-expand collapsible filter panel when URL-based filters are detected on initial page load
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx:
- Around line 284-296: activeFilterCount currently only counts the time filter
when both filters.startTime and filters.endTime are present, which can mismatch
hasStatsFilters and timeActiveCount; change the time check in
usage-logs-view-virtualized.tsx (activeFilterCount) to treat a partial time
range as active (e.g., count if filters.startTime !== undefined ||
filters.endTime !== undefined), and also add symmetry by either updating the
timeActiveCount logic in usage-logs-filters.tsx or normalize URL/filters earlier
(coerce to both undefined if only one of filters.startTime/filters.endTime is
present) so both places use the same presence rule.
| const activeFilterCount = useMemo(() => { | ||
| let count = 0; | ||
| if (filters.userId !== undefined) count++; | ||
| if (filters.keyId !== undefined) count++; | ||
| if (filters.providerId !== undefined) count++; | ||
| if (filters.sessionId) count++; | ||
| if (filters.startTime && filters.endTime) count++; | ||
| if (filters.statusCode !== undefined || filters.excludeStatusCode200) count++; | ||
| if (filters.model) count++; | ||
| if (filters.endpoint) count++; | ||
| if (filters.minRetryCount !== undefined && filters.minRetryCount > 0) count++; | ||
| return count; | ||
| }, [filters]); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the actual content at the specified lines in usage-logs-view-virtualized.tsx
echo "=== Checking usage-logs-view-virtualized.tsx lines 265-296 ==="
rg -n "activeFilterCount|hasStatsFilters|statsFilters" src/app/*/dashboard/logs/_components/usage-logs-view-virtualized.tsx -A 15 -B 2Repository: ding113/claude-code-hub
Length of output: 3481
🏁 Script executed:
#!/bin/bash
# Look for usage-logs-filters.tsx to understand filter behavior
echo "=== Finding usage-logs-filters file ==="
fd "usage-logs-filters" --type fRepository: ding113/claude-code-hub
Length of output: 173
🏁 Script executed:
#!/bin/bash
# Check for the i18n key t("title.filterCriteria")
echo "=== Searching for i18n key ==="
rg "title.filterCriteria" --type ts --type tsxRepository: ding113/claude-code-hub
Length of output: 142
🏁 Script executed:
#!/bin/bash
# Check locale files for the key
echo "=== Checking locale files ==="
fd ".*\.json" -t f | grep -i locale | head -20Repository: ding113/claude-code-hub
Length of output: 96
🏁 Script executed:
#!/bin/bash
# Check usage-logs-filters.tsx for startTime/endTime handling
echo "=== Checking filter form logic ==="
cat src/app/*/dashboard/logs/_components/usage-logs-filters.tsx | head -200Repository: ding113/claude-code-hub
Length of output: 6463
🏁 Script executed:
#!/bin/bash
# Find locale directory
echo "=== Finding locale directory ==="
fd "messages" --type d
fd "locales" --type d
find . -type d -name "*locale*" | head -10Repository: ding113/claude-code-hub
Length of output: 186
🏁 Script executed:
#!/bin/bash
# Search for i18n keys more carefully
echo "=== Searching for filterCriteria key ==="
rg "filterCriteria" -t json
rg "filterCriteria"Repository: ding113/claude-code-hub
Length of output: 1928
🏁 Script executed:
#!/bin/bash
# Check the type definitions for filters
echo "=== Checking filters type ==="
rg -n "type.*Filter" src/app/*/dashboard/logs/_components/usage-logs-view-virtualized.tsx -B 2 -A 10Repository: ding113/claude-code-hub
Length of output: 636
🏁 Script executed:
#!/bin/bash
# Check the exact structure of dashboard.json to see if "title" nesting exists
echo "=== Checking dashboard.json structure for filterCriteria ==="
grep -A 2 -B 2 "filterCriteria" messages/en/dashboard.jsonRepository: ding113/claude-code-hub
Length of output: 372
🏁 Script executed:
#!/bin/bash
# Check the full context around filterCriteria in locale files
echo "=== Full context in zh-CN dashboard.json ==="
cat messages/zh-CN/dashboard.json | head -100Repository: ding113/claude-code-hub
Length of output: 2895
🏁 Script executed:
#!/bin/bash
# Look at TimeFilters component to understand startTime/endTime behavior
echo "=== Checking TimeFilters component ==="
fd "time-filters" --type fRepository: ding113/claude-code-hub
Length of output: 174
🏁 Script executed:
#!/bin/bash
# Check TimeFilters component to see if startTime and endTime can be set independently
echo "=== Checking TimeFilters implementation ==="
cat src/app/*/dashboard/logs/_components/filters/time-filters.tsxRepository: ding113/claude-code-hub
Length of output: 6184
🏁 Script executed:
#!/bin/bash
# Also check if there are any validations that enforce both being set together
echo "=== Searching for startTime/endTime validation logic ==="
rg -n "startTime|endTime" src/app/*/dashboard/logs/_components/filters/time-filters.tsx -B 2 -A 2Repository: ding113/claude-code-hub
Length of output: 3783
第 290 行的时间范围判断逻辑存在潜在不一致
第 290 行要求 filters.startTime && filters.endTime 同时存在才将时间范围计为 1 个过滤器,但第 282 行的 hasStatsFilters 基于单个字段检查(使用 Object.values().some(v => v !== undefined && v !== false))。虽然 TimeFilters 组件的日期选择器确保两个字段通常成对设置/清除,但在 URL 参数被直接修改的情况下,可能出现仅 startTime 被设置而 endTime 未设置的状态,导致统计面板显示(hasStatsFilters = true)而 badge 计数为 0(activeFilterCount = 0)的视觉不一致。
同时,usage-logs-filters.tsx 中的 timeActiveCount(第 107-111 行)使用相同的逻辑,保持了一致性,但这种设计仍应明确文档说明或加强 URL 参数验证,确保时间范围始终成对出现。
验证结果:i18n 键 t("title.filterCriteria") 已存在于全部 5 种语言的 messages/[locale]/dashboard.json 中,无需修改。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
around lines 284 - 296, activeFilterCount currently only counts the time filter
when both filters.startTime and filters.endTime are present, which can mismatch
hasStatsFilters and timeActiveCount; change the time check in
usage-logs-view-virtualized.tsx (activeFilterCount) to treat a partial time
range as active (e.g., count if filters.startTime !== undefined ||
filters.endTime !== undefined), and also add symmetry by either updating the
timeActiveCount logic in usage-logs-filters.tsx or normalize URL/filters earlier
(coerce to both undefined if only one of filters.startTime/filters.endTime is
present) so both places use the same presence rule.
Half-width parentheses were introduced in ding113#798, breaking the zh-tw-dashboard-parentheses test.
| function Separator() { | ||
| return <span className="text-border hidden sm:inline">|</span>; | ||
| } |
There was a problem hiding this comment.
separator color should use proper border color variable
The separator uses text-border which applies the border color to text, creating a grey separator. However, for semantic correctness and proper theming, use text-muted-foreground or create a proper divider element with border styling.
| function Separator() { | |
| return <span className="text-border hidden sm:inline">|</span>; | |
| } | |
| function Separator() { | |
| return <span className="text-muted-foreground/30 hidden sm:inline">|</span>; | |
| } |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/dashboard/logs/_components/usage-logs-stats-panel.tsx
Line: 133-135
Comment:
separator color should use proper border color variable
The separator uses `text-border` which applies the border color to text, creating a grey separator. However, for semantic correctness and proper theming, use `text-muted-foreground` or create a proper divider element with border styling.
```suggestion
function Separator() {
return <span className="text-muted-foreground/30 hidden sm:inline">|</span>;
}
```
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.| <ChevronDown | ||
| className={`ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200 ${isFiltersOpen ? "rotate-180" : ""}`} | ||
| /> |
There was a problem hiding this comment.
avoid inline template literal for dynamic class
String interpolation with template literals for dynamic classes can break certain CSS tooling (like Tailwind's purge scanner in older configs). Use cn() utility or conditional class approach:
| <ChevronDown | |
| className={`ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200 ${isFiltersOpen ? "rotate-180" : ""}`} | |
| /> | |
| <ChevronDown | |
| className={cn( | |
| "ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200", | |
| isFiltersOpen && "rotate-180" | |
| )} | |
| /> |
You'll need to import cn from @/lib/utils.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
Line: 327-329
Comment:
avoid inline template literal for dynamic class
String interpolation with template literals for dynamic classes can break certain CSS tooling (like Tailwind's purge scanner in older configs). Use `cn()` utility or conditional class approach:
```suggestion
<ChevronDown
className={cn(
"ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200",
isFiltersOpen && "rotate-180"
)}
/>
```
You'll need to import `cn` from `@/lib/utils`.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.5e39dca to
5e2c8d9
Compare
| <Badge variant="secondary">{activeFilterCount}</Badge> | ||
| ) : null} | ||
| <ChevronDown | ||
| className={`ml-auto h-4 w-4 text-muted-foreground transition-transform duration-200 ${isFiltersOpen ? "rotate-180" : ""}`} |
There was a problem hiding this comment.
template literal with string interpolation can break Tailwind's class detection in some tooling configurations; consider using conditional classes or cn() utility
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx
Line: 328
Comment:
template literal with string interpolation can break Tailwind's class detection in some tooling configurations; consider using conditional classes or `cn()` utility
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.
关联 Issue
Closes #766
变更内容
基于上游 v0.5.8 rebase(包含 #795、#794、#793 等)。在上游
hasStatsFilters性能优化(无筛选条件时隐藏统计面板,避免全表聚合查询)的基础上,叠加 #766 要求的首屏空间优化。具体改动
ActiveSessionsList新增compactEmptyprop(默认false,不影响其他调用方),无活跃会话时压缩为单行显示与上游的兼容性
hasStatsFilters性能守卫(来自29d05b22)——仅在有筛选条件时才渲染统计面板影响范围
ActiveSessionsList新增 propfalse,其他调用方行为不变涉及文件
src/app/[locale]/dashboard/logs/_components/usage-logs-stats-panel.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsxsrc/app/[locale]/dashboard/logs/_components/usage-logs-sections.tsxsrc/components/customs/active-sessions-list.tsx检查清单
devbun run typecheck通过bun run lint通过bun run build通过效果图
Greptile Summary
This PR successfully reduces above-fold space on the logs page by compacting the stats panel from a 4-card grid (~150px) to an inline single row (~40px), collapsing the filter panel by default with badge count indicator, and compressing the empty active sessions state to a single line. The changes preserve all existing functionality while improving screen real estate usage.
Key changes:
Collapsiblewith auto-expand when URL filters are detectedActiveSessionsListgains optionalcompactEmptyprop (defaultfalse, backward compatible)Compatibility:
hasStatsFiltersperformance guard from v0.5.8ActiveSessionsListunaffected by default prop valueConfidence Score: 4/5
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Logs Page] --> B[Active Sessions Section] A --> C[Stats Panel] A --> D[Filter Panel] A --> E[Usage Records Table] B -->|compactEmpty=true| B1{Has Active Sessions?} B1 -->|Yes| B2[Full Card Layout] B1 -->|No| B3[Compact Single Line] C -->|hasStatsFilters?| C1{Has Filter Criteria?} C1 -->|Yes| C2[Inline Stats Display] C1 -->|No| C3[Hidden] D --> D1[Collapsible Component] D1 -->|URL Filters| D2{activeFilterCount > 0?} D2 -->|Yes| D3[Auto-expand on mount] D2 -->|No| D4[Collapsed by default] D1 -->|Collapsed| D5[Single line + Badge count] D1 -->|Expanded| D6[Full filter form] C2 --> C4[4 inline stats separated by pipes] C4 --> C5[Requests, Cost, Tokens, Cache] style B3 fill:#e1f5e1 style C2 fill:#e1f5e1 style D3 fill:#e1f5e1 style D5 fill:#e1f5e1Last reviewed commit: 5e2c8d9